#include <png.h>
#include <stdio.h>
#include <malloc.h>

#define RGB8(r, g, b) 0

unsigned short * palette = 0;
unsigned char * data = 0;
static void user_read_fn(png_structp png_ptr, unsigned char *dest, png_size_t size)
{
  FILE * f = (FILE*)png_get_io_ptr(png_ptr);
  size_t n = fread((char*)dest, size, 1, f);
  if(size && (n == 0)) {
    printf("Read error: invalid or corrupted PNG file\n");
  }
}

void readPng(const char * filename)
{
   FILE * f = 0;
   f = fopen(filename, "rb");
   if (!f) { return; }

   png_structp png_ptr= png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
   if (png_ptr == NULL) { return; }

   /* Allocate/initialize the memory for image information.  REQUIRED. */
   png_infop info_ptr = png_create_info_struct(png_ptr);
   if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); return; }

   /* Set error handling if you are using the setjmp/longjmp method (this is
    * the normal method of doing things with libpng).  REQUIRED unless you
    * set up your own error handlers in the png_create_read_struct() earlier.
    */
   if (setjmp(png_jmpbuf(png_ptr))) {
      /* Free all of the memory associated with the png_ptr and info_ptr */
      png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
      /* If we get here, we had a problem reading the file */
      printf("problem reading the file\n");
      return;
   }

   // compiled png with NO_STIO - avoids sprintf madness.
   png_set_read_fn(png_ptr, (void *)f, user_read_fn);
   // read the easy way
   png_read_info(png_ptr, info_ptr);
   // alpha images require this:
   png_set_strip_alpha(png_ptr);

   if(info_ptr->color_type == PNG_COLOR_TYPE_GRAY && info_ptr->bit_depth < 8)
   {
     printf("Handle gray 1,2,4\n");
     //png_set_gray_1_2_4_to_8(png_ptr);
     png_set_expand(png_ptr);
   }

   if (info_ptr->bit_depth < 8) { printf("Expand\n"); png_set_packing(png_ptr); }
   // paletted images require this:
   if(info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
   {
     printf("Handle palette\n");
#if 0
     // remove palette info
     png_set_palette_to_rgb(png_ptr);
#else
     png_colorp png_palette;
     int palette_entries;
     png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries);
     printf(" %d palette entries\n", palette_entries);
     palette = (unsigned short*)malloc(sizeof(unsigned short) * palette_entries);
     int i;
     for (i = 0; i < palette_entries; ++i)
     {
       palette[i] = RGB8(png_palette[i].red, png_palette[i].green, png_palette[i].blue);
     }
#endif
   }
   printf("handle interlace \n");
   png_set_interlace_handling(png_ptr);
   printf("update info \n");
   png_read_update_info(png_ptr, info_ptr);
   printf("Extract data\n");
   int height = info_ptr->height;
   data = (unsigned char*)malloc( info_ptr->rowbytes * height);
   png_bytep * row_pointers = (png_bytep *)malloc(sizeof(png_bytep) * height);
   if (row_pointers == 0)
   {
     printf("OUT OF MEM!\n");
     return;
   }
   int y;
   for (y = 0; y < height; y++)
   {
     row_pointers[y] = (png_bytep)(data + (info_ptr->rowbytes * y));
   }

   printf("read image\n");
   png_read_image(png_ptr, row_pointers);

   // clear up row pointers - now everything is in data
   printf("free pointer\n");
   free(row_pointers);
   printf("destroy read struct\n");
   png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
}

int main(int argc, char * argv[])
{
  readPng("smiley.png");
  return 0;
}